SPB Git

Repository Public

route.ts 1.3 KB 20 lines typescript

1import { sql } from "@/lib/db";2import { sameOrigin, visitorHash } from "@/lib/auth";3import { rateLimit } from "@/lib/ratelimit";4import { bad } from "@/lib/api";56/** POST /api/films/:slug/react — anonymous like / unlike, one per visitor per day-hash. */7export async function POST(req: Request, ctx: { params: Promise<{ slug: string }> }) {8  if (!sameOrigin(req)) return bad("bad_origin", 403);9  const visitor = await visitorHash();10  if (!(await rateLimit(`react:${visitor}`, 40, 3600))) return bad("rate_limited", 429);11  const body = (await req.json().catch(() => null)) as { like?: unknown } | null;12  if (!body || typeof body.like !== "boolean") return bad("invalid_body");13  const [film] = await sql<{ id: string }[]>`SELECT id FROM films WHERE slug = ${(await ctx.params).slug} AND status = 'published'`;14  if (!film) return bad("not_found", 404);15  if (body.like) await sql`INSERT INTO reactions (film_id, visitor, kind) VALUES (${film.id}, ${visitor}, 'like') ON CONFLICT DO NOTHING`;16  else await sql`DELETE FROM reactions WHERE film_id = ${film.id} AND visitor = ${visitor} AND kind = 'like'`;17  const [r] = await sql<{ likes: number }[]>`UPDATE films SET likes = (SELECT COUNT(*) FROM reactions WHERE film_id = ${film.id}) WHERE id = ${film.id} RETURNING likes`;18  return Response.json({ likes: r.likes }, { headers: { "Cache-Control": "no-store" } });19}20
Files main
Menu

Theme